﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

namespace UnityNetwork
{
    public class TCPPeer
    {
        public enum MessageID
        {
            OnNewConnection=1, // 接收新的连接
            OnConnected=2,  // 连接到服务器
            OnConnectFail=3,  // 连接失败 
            OnDisconnect=4, //断开连接
        }

        protected MyEventHandler handler;

        public TCPPeer(MyEventHandler handler)
        {
            this.handler = handler;
        }

        public void Listen(int port,int backlog=128)
        {
            IPEndPoint ipe = new IPEndPoint(IPAddress.Any, port);
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                socket.Bind(ipe);
                socket.Listen(backlog);
                socket.BeginAccept(new System.AsyncCallback(ListenCallback), socket);
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        void ListenCallback(System.IAsyncResult ar)
        {
            Socket listener = (Socket)ar.AsyncState;
            try
            {
                Socket client = listener.EndAccept(ar);

                handler.AddPacket(new Packet((short)MessageID.OnNewConnection, client));
                Packet packet = new Packet(0, client);
                client.BeginReceive(packet.buffer, 0, Packet.headerLength, SocketFlags.None, new System.AsyncCallback(ReceiveHeader), packet);
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
            listener.BeginAccept(new System.AsyncCallback(ListenCallback), listener);
        }

        public Socket Connect(string ip,int port)
        {
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), port);
            try
            {
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                socket.BeginConnect(ipe, new System.AsyncCallback(ConnectionCallback), socket);

                return socket;
            }
            catch(Exception e)
            {
                Debug.Log(e.Message);
                handler.AddPacket(new Packet((short)MessageID.OnConnectFail));
                return null;
            }
        }

        void ConnectionCallback(System.IAsyncResult ar)
        {
            Socket client = (Socket)ar.AsyncState;
            try
            {
                client.EndConnect(ar);
                handler.AddPacket(new Packet((short)MessageID.OnConnected, client));
                Packet packet = new Packet(0, client);
                client.BeginReceive(packet.buffer, 0, Packet.headerLength, SocketFlags.None, new System.AsyncCallback(ReceiveHeader), packet);
            }
            catch(Exception e)
            {
                Debug.Log(e.Message);
                handler.AddPacket(new Packet((short)MessageID.OnConnectFail, client));
            }
        }

        void ReceiveHeader(System.IAsyncResult ar)
        {
            Packet packet = (Packet)ar.AsyncState;
            try
            {
                int read = packet.sk.EndReceive(ar);
                if (read < 1)
                {
                    handler.AddPacket(new Packet((short)MessageID.OnDisconnect, packet.sk));
                    return;
                }
                packet.readLength += read; // 记录收到的数据长度
                if (packet.readLength < Packet.headerLength)
                {
                    packet.sk.BeginReceive(packet.buffer, packet.readLength, Packet.headerLength - packet.readLength, SocketFlags.None, new System.AsyncCallback(ReceiveHeader), packet);
                }
                else
                {
                    packet.DecodeHeader();
                    packet.readLength = 0;
                    packet.sk.BeginReceive(packet.buffer, Packet.headerLength, packet.bodyLength, SocketFlags.None, new System.AsyncCallback(ReceiveBody), packet);
                }
            }
            catch(Exception e)
            {
                Debug.Log(e.Message);
                handler.AddPacket(new Packet((short)MessageID.OnDisconnect, packet.sk));
            }
        }

        void ReceiveBody(IAsyncResult ar)
        {
            Packet packet = (Packet)ar.AsyncState;
            try
            {
                int read = packet.sk.EndReceive(ar);
                if (read < 1)
                {
                    handler.AddPacket(new Packet((short)MessageID.OnDisconnect, packet.sk));
                    return;
                }
                packet.readLength += read;
                if (packet.readLength < packet.bodyLength)
                {
                    packet.sk.BeginReceive(packet.buffer, Packet.headerLength + packet.readLength, packet.bodyLength - packet.readLength, SocketFlags.None, new System.AsyncCallback(ReceiveBody), packet);
                }
                else
                {
                    Packet newpackert= new Packet(packet);
                    handler.AddPacket(newpackert);
                    packet.ResetParams();
                    packet.sk.BeginReceive(packet.buffer, 0, Packet.headerLength, SocketFlags.None, new System.AsyncCallback(ReceiveHeader), packet);
                }

            }
            catch (Exception e)
            {
                Debug.Log(e.Message);
                handler.AddPacket(new Packet((short)MessageID.OnDisconnect, packet.sk));
            }
        }

        public static void Send(Socket sk,Packet packet)
        {
            if(!packet.encoded)
            {
                throw new Exception("无效数据包");
            }
            NetworkStream ns;
            lock(sk)
            {
                ns = new NetworkStream(sk);
                if(ns.CanWrite)
                {
                    try
                    {
                        ns.BeginWrite(packet.buffer, 0, Packet.headerLength + packet.bodyLength, new System.AsyncCallback(SendCallback), ns);

                    }
                    catch(Exception er)
                    {
                        Console.WriteLine(er.Message);
                    }
                }
            }
        }

        private static void SendCallback(IAsyncResult ar)
        {
            NetworkStream ns = (NetworkStream)ar.AsyncState;
            try
            {
                ns.EndWrite(ar);
                ns.Flush();
                ns.Close();
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message); 
            }
        }

        public static IPHostEntry GetWWWAddrIP(string wwwAddr)
        {
            IPHostEntry entry = Dns.GetHostEntry(wwwAddr);
            return entry;
        }
    }
}
